<?php
declare(strict_types=1);

namespace Core\HyperfUnit;

use Zms\Request\Http;
use Exception;
use Hyperf\HttpServer\Contract\RequestInterface;
use const FILTER_FLAG_IPV4;
use const FILTER_FLAG_IPV6;
use const FILTER_VALIDATE_IP;

class Ip
{

    /**
     * 获取客户端IP
     * @param bool $allowIpv6 是否允许IPV6类型的IP
     * @return string
     * @author 竹林风@875384189 2021/3/11 11:56
     */
    public static function getIp(bool $allowIpv6 = true): string
    {
        try {
            $ServerParams = container(RequestInterface::class)->getHeaders();
            $ip = '127.0.0.1';
            if (isset($ServerParams['x-real-ip'])) {
                $ip = $ServerParams['x-real-ip'][0];
            } elseif (isset($ServerParams['x-forwarded-for'])) {
                $ip = $ServerParams['x-forwarded-for'][0];
            } elseif (isset($ServerParams['remote-host'])) {
                $ip = $ServerParams['remote-host'][0];
            }
            if ($allowIpv6 === true) return $ip;
            return strlen($ip) <= 15 ? $ip : '127.0.0.6';
        } catch (Exception) {
            return '127.0.0.1';
        }
    }

    /**
     * 获取IP类型
     * @param $ip
     * @return string
     */
    public static function type($ip): string
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return 'ipv4';
        } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            return 'ipv6';
        }
        return '';
    }


    /**
     * Ip地址转码
     * @param string $ip
     * @param string $default
     * @return string
     */
    public static function ipToNumber(string $ip, string $default = '0'): string
    {
        $ipnum = '';
        switch (self::type($ip)) {
            case 'ipv4':
                $ipnum = ip2long($ip);
                break;
            case 'ipv6':
                $ip_n = inet_pton($ip);
                $bin = '';
                for ($bit = strlen($ip_n) - 1; $bit >= 0; $bit--) {
                    $bin = sprintf('%08b', ord($ip_n[$bit])) . $bin;
                }
                $ipnum = '0';
                for ($i = 0; $i < strlen($bin); $i++) {
                    $ipnum = bcmul($ipnum, '2', 0);
                    $ipnum = bcadd($ipnum, $bin[$i], 0);
                }
                break;
        }
        return (string)($ipnum ?: $default);
    }


    /**
     * IP解码
     * @param string $ipcode
     * @return string
     */
    public static function numberToIp(string $ipcode): string
    {
        if (strlen($ipcode) > 10) {//ipv4
            $bin = '';
            do {
                $bin = bcmod($ipcode, '2') . $bin;
                $ipcode = bcdiv($ipcode, '2');
            } while (bccomp($ipcode, '0'));
            $bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
            $ip = array();
            for ($bit = 0; $bit <= 7; $bit++) {
                $bin_part = substr($bin, $bit * 16, 16);
                $ip[] = dechex(bindec($bin_part));
            }
            $ip = implode(':', $ip);
        } else {
            $ip = long2ip(intval($ipcode));
        }
        return inet_ntop(inet_pton($ip)) ?: '0.0.0.0';
    }


    /**
     * 获取IP归属地
     */
    public static function ascription(string $ip): string
    {
        if ($ip === '127.0.0.1' || $ip === '0.0.0.0') {
            return '本机地址';
        }
        return Callback::cache('ip:' . md5($ip), function () use ($ip) {
            if ($ip) {
                $body = Http::instance()->execute("http://opendata.baidu.com/api.php?query={$ip}&co=&resource_id=6006&oe=utf8")->jsonToArray();
                if ($body) {
                    if ($body && isset($body['status']) && intval($body['status']) === 0) {
                        return $body['data'][0]['location'] ?? '';
                    }
                }
            }
            return '';
        });
    }
}
